package com.springboot.jwt.realm;

import com.springboot.jwt.common.JWTToken;
import com.springboot.jwt.common.JWTUtils;
import com.springboot.jwt.common.RedisUtils;
import com.springboot.jwt.dao.PermissionDao;
import com.springboot.jwt.dao.RoleDao;
import com.springboot.jwt.pojo.Role;
import com.springboot.jwt.pojo.User;
import com.springboot.jwt.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

/**
 * @Author: Jyf
 * @Date: 2020/12/17 10:44
 */
@Slf4j
@Component
public class UserRealm extends AuthorizingRealm {

    @Autowired
    private UserService userService;
    @Autowired
    RoleDao roleDao;
    @Autowired
    PermissionDao permissionDao;
    @Resource
    RedisUtils redisUtils;

    private static final String PREFIX = "USER_LOGIN_FAIL:";

    /**
     * 大坑！，必须重写此方法，不然Shiro会报错
     */
    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof JWTToken;
    }

    /**
     * 只有当需要检测用户权限的时候才会调用此方法，例如checkRole,checkPermission之类的
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        System.out.println("缓存：" + this.getAuthorizationCache().getClass());
        log.info("用户授权...");
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        User user = (User) principals.getPrimaryPrincipal();
        Role role = roleDao.getOne(user.getRoleId());
        if (role != null) {
            if ("admin".equals(role.getName())) {
                authorizationInfo.addStringPermission("*:*");
            } else {
                authorizationInfo.addRole(role.getName());
                authorizationInfo.addStringPermission(permissionDao.getOne(role.getId()).getDescription());
            }

        }
//        if (user.getType() == 0) {
//            //超级管理员，系统最高权限
//            authorizationInfo.addStringPermission("*:*");
//        } else {
//            for (Role role : user.getRoles()) {                                 //获取 角色
//                authorizationInfo.addRole(role.getName());                      //添加 角色
//                for (Permission permission : role.getPermissions()) {           //获取 权限
//                    authorizationInfo.addStringPermission(permission.getName());//添加 权限
//                }
//            }
//        }
        return authorizationInfo;
    }

    /**
     * 默认使用此方法进行登录：用户名正确与否验证，错误抛出异常即可。
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken tok) throws AuthenticationException {
        System.out.println("缓存：" + this.getAuthenticationCache().getClass());
        // 解密获得username，用于和数据库进行对比
        String token = (String) tok.getCredentials();
        String username = JWTUtils.getUsername(token);

        if (username == null) {
            throw new AuthenticationException("token异常！");
        }

        User userBean = userService.findByUsername(username);

        if (userBean == null) {
            throw new AuthenticationException("账号不存在!");
        }

        int errorNum = ifLockUser(username);

        if (!JWTUtils.verify(token, username, userBean.getPassword())) {
            //添加一次错误次数
            redisUtils.set(PREFIX + username, String.valueOf(++errorNum), 600L);
            throw new AuthenticationException("密码错误! 连续输错5次将锁定当前账户！当前错误次数：" + errorNum);
        }
        //清空错误次数
        redisUtils.remove(PREFIX + username);
        return new SimpleAuthenticationInfo(userBean, token, getName());
    }

    /**
     * 判断账户密码输入错误次数是否被锁定
     */
    public int ifLockUser(String userName) {
        //初始化错误登录次数
        int errorNum = 0;
        //从数据库里获取错误次数
        String errorTimes = (String) redisUtils.get(PREFIX + userName);
        if (errorTimes != null && errorTimes.trim().length() > 0) {
            //如果得到的字符串不为空不为空
            errorNum = Integer.parseInt(errorTimes);
        }
        //如果用户错误登录次数超过十次
        if (errorNum >= 5) {
            //抛出账号锁定异常类
            throw new AuthenticationException("密码连续输错5次！当前账户已被锁定十分钟！");
        }
        return errorNum;
    }
}
